#pragma once
#include <queue>
#include <thread>
#include "Task.hpp"

#define NUM 6

class ThreadPool
{
private:
    ThreadPool(int num = NUM)
        : _num(num),
          _stop(false)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ThreadPool(const ThreadPool&){}

public:
    bool InitThreadPool()
    {
        for (int i = 0; i < _num; i++)
        {
            pthread_t tid;
            if (pthread_create(&tid, nullptr, ThreadRoutinue, this) != 0)
            {
                LOG(FATAL, "create thread pool error");
                return false;
            }
        }

        LOG(FATAL, "create thread pool success");
        return true;
    }

    bool IsStop()
    {
        return _stop;
    }

    static void* ThreadRoutinue(void* args)
    {
        ThreadPool *tp = (ThreadPool *)(args);
        while (true)
        {
            Task task;
            tp->Lock();
            // while 防止误唤醒
            while (tp->TaskQueueIsEmpty())
            {
                tp->ThreadWait(); // 醒来时，一定占有锁
            }
            tp->UnLock();
            tp->PopTask(task);
            task.ProcessOn();
        }
    }

    void ThreadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void ThreadWakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    void Lock()
    {
        pthread_mutex_lock(&_mutex);
    }

    void UnLock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void PushTask(const Task& task)
    {
        Lock();
        _task_queue.push(task);
        UnLock();
        ThreadWakeUp();
    }

    bool TaskQueueIsEmpty()
    {
        return _task_queue.size() == 0 ? true : false;
    }

    void PopTask(Task& task)
    {
        task = _task_queue.front();
        _task_queue.pop();
    }

    static ThreadPool* GetInstance()
    {
        static pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER;
        if (_instance == nullptr)
        {
            pthread_mutex_lock(&_lock);
            if (_instance == nullptr)
            {
                _instance = new ThreadPool();
                _instance->InitThreadPool();
            }
            pthread_mutex_unlock(&_lock);
        }

        return _instance;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _num;
    bool _stop;
    std::queue<Task> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    static ThreadPool* _instance;
};

ThreadPool* ThreadPool::_instance = nullptr;
